# gozerbot/eventbase.py
#
#

""" 
    This module implements the EventBase class from which all other 
    events inherit. 

    :vars: defaultevent

"""

__copyright__ = 'this file is in the public domain'

# ==============
# IMPORT SECTION

# gozerbot imports
from xmpp.core import XMLDict
from utils.log import rlog
from utils.generic import stripident, fix_format, fromenc, toenc
from utils.lazydict import LazyDict
from gozerbot.stats import stats
from gozerbot.config import config

# basic imports
import time, re, types, copy

from simplejson import loads, dumps

# END IMPORT
# ==========

# ============
# LOCK SECTION

# no locks

# END LOCK
# ========

## START 

# used to copy data
cpy = copy.deepcopy

def makeargrest(event):

    """
        set arguments and rest attributes of an event.

        :param event: event to manipulate

    """

    # arguments 
    try:
        event.args = event.txt.split()[1:]
    except ValueError:
        event.args = []

    # txt after command
    try:
        cmnd, event.rest = event.txt.split(' ', 1)
    except ValueError:
        event.rest = ""   

    # the command 
    event.command = event.txt.split(' ')[0]

class EventBase(XMLDict):

    """ 
        Base class for all events. Inherit from this. 

        :param event: event to clone from (optional)
        :type event: gozerbot.event.EventBase
        :param bot: bot on which this event occured
        :type bot: gozerbot.botbase.BotBase

    """


    def __init__(self, event={}, bot=None):
        self.server = ""
        XMLDict.__init__(self)
        self.threaded = False
        self.onlyqueues = False
        self.orig = event # the original data .. SET BY HANDLER 
        self.type = 'chat' # type of event
        self.cbtype = '' # callback type
        self.jabber = False # set if event is jabber event
        self.groupchat = False # set if event is groupchat
        self.botoutput = False # set if event is bot output
        self.cmnd = None # the event command
        self.prefix = u"" # txt before the command
        self.postfix = u"" # txt after the command
        self.target = u"" # target to give reponse to
        self.arguments = [] # arguments of event
        self.nick = u"" # nick of user originating the event 
        self.user = u""  # user originating the event
        self.ruserhost = u"" # real userhost 
        self.userhost = u"" # userhost that might change
        self.stripped = u"" # stripped JID 
        self.resource = u"" # resource part of JID
        self.origin = u"" # source of the event
        self.origchannel = u"" # original channel
        self.channel = u"" # channel .. might change
        self.origtxt = u"" # original txt
        self.txt = u"" # text .. might change
        self.command = u"" # the bot command if any
        self.remoteout = u""
        self.remotecmnd = False
        self.usercmnd = False
        self.alias = u"" # set to alias if one is used
        self.aliased = u"" # set if commadn is aliased
        self.time = time.time() # event creation time
        self.msg = False # set if event is a private message
        self.args = [] # arguments of command
        self.rest = u"" # txt following the command
        self.usercmnd = 0 # set if event is a command
        self.bot = bot # the bot where the event originated on
        self.sock = None # socket (set in DCC chat)
        self.allowqueue = True # allow event to be used in pipeline
        self.closequeue = True # cloase event queues when execution has ended
        self.inqueue = None # queue used as input in pipeline
        self.queues = [] # output queues (only used when set)
        self.printto = None # target to printto
        self.speed = 0 # speed with which this event needs to be processed
        self.groups = None # set if event triggers a RE callback
        self.cc = u"" # control character
        self.jid = None # JID of used originating the event
        self.jidchange = None # set is event changes jid
        self.conn = None # connection of bot originating the event
        self.denied = False # set if command is denied
        self.iscallback = False
        self.isresponse = False # set if event is a reponse
        self.isrelay = False
        self.isremote = False
        self.isdcc = False # set if event is DCC related
        self.isctcp = False # set if event is an ACTION
        self.options = LazyDict() # options dict on the event
        self.optionset = [] # list of options set
        self.filter = [] # filter list to use on output

        # wave stuff
        self.wave = False
        self.properties = None
        self.context = None
        self.eventin = None
        self.wavelet = None

        # GAE stuff
        self.google = False
        self.xmppgae = False
        self.response = None
        self.request = None

        # xmpp stuff
        self.host = self.server
        self.xml = u""
        self.realjid = u""
        self.fromm = u""
        self.to = u""
        self.type = u""
        self.id = 0
        self.subject = u""
        self.body = u""
        self.error = u""
        self.errorcode = u""
        self.html = u""
        self.thread = u""
        self.x = {}

        if event:
            self.copyin(event)

        self.toirc() 

        # stats
        stats.up('events', 'created')

    def __copy__(self):
        return EventBase(self)

    def __deepcopy__(self, bla):
        return EventBase(self)

    def toirc(self):

        """
            set ircevent compat attributes.

            .. literalinclude:: ../../gozerbot/eventbase.py
                :pyobject: EventBase.toirc
        """

        self.jidchange = False
        self.cmnd = 'Message'

        try:
            self.resource = self.fromm.split('/')[1]
        except IndexError:
            pass

        self.channel = self['fromm'].split('/')[0]
        self.origchannel = self.channel
        self.nick = self.resource

        #try:
        #    self.jid = bot.jids[self.channel][self.resource]
        #    self.jidchange = True
        #except KeyError:
        #     pass

        self.jid = self.fromm
        self.ruserhost = self.jid
        self.userhost = self.jid
        self.stripped = self.jid.split('/')[0]
        self.printto = self.channel
        self.target = self.printto

        for node in self.subelements:
            try:
                self.txt = node.body.data
            except (AttributeError, ValueError):
                continue

        self.origtxt = self.txt
        self.time = time.time()

        if self.type == 'groupchat':
            self.groupchat = True
            if self.jidchange:
                self.userhost = self.stripped
        else:
            self.groupchat = False
            self.userhost = self.stripped

        self.msg = not self.groupchat

    def copyin(self, ievent):

        """
            copy in an event.

            :param ievent: event to copy in
            :type ievent: EventBase

            .. literalinclude:: ../../gozerbot/eventbase.py
                :pyobject: EventBase.copyin

        """
        self.update(ievent)

        if ievent.has_key('options'):
            self.options = dict(ievent.options)        

        if ievent.has_key('queues'):
            self.queues = list(ievent.queues)

        return self

    def filtered(self, txt):

        """
            see if txt if filtered on this event.

            :param txt: text to check if its filtered
            :type txt: string

            .. literalinclude:: ../../gozerbot/eventbase.py
                :pyobject: EventBase.filtered

        """

        if not self.filter:
            return False

        for filter in self.filter:
            if filter in txt:
                return False

        return True

    def parse(self, bot, rawstr):

        """
            parse raw string into event.  overload this.

            :param bot: bot on which event is triggered
            :type bot: gozerbot.botbase.BotBase
            :param rawstr: string as recieved on the socket
            :type rawstr: string

        """

        pass

    def make_response(self, txt, result=None, nick=None, dot=False, nritems=False, nr=False, fromm=None, private=False, how=''):


        # don't reply is result is empty list
        if result == []:
            return

        # stats
        stats.up('events', 'replies')
        if not how:
            try:
                how = self.options['--how']        
            except KeyError:
                how = 'msg'

        # init
        restxt = ""
        splitted = []

        # make reply if result is a dict
        if type(result) == types.DictType:
            for i, j in result.iteritems():
                if type(j) == types.ListType:
                    try:
                        z = ' .. '.join(j)
                    except TypeError:
                        z = unicode(j)
                else:
                    z = j
                res = "%s: %s" % (i, z)
                splitted.append(res)
                if dot == True:
                    restxt += "%s%s" % (res, ' .. ')
                else:
                    restxt += "%s %s" % (dot or ' ', res)
            if restxt:
                if dot == True:
                    restxt = restxt[:-6]
                elif dot:
                    restxt = restxt[:-len(dot)]

        lt = False # set if result is list

        # set vars if result is a list
        if type(txt) == types.ListType and not result:
            result = txt
            origtxt = u""
            lt = True
        else:
            origtxt = txt

        if result:
            lt = True

        # if queues are set write output to them
        if self.queues:
            for i in self.queues:
                if splitted:
                    for item in splitted:
                        i.put_nowait(item)
                elif restxt:
                    i.put_nowait(restxt)
                elif lt:
                    for j in result:
                        i.put_nowait(j)
                else:
                    i.put_nowait(txt)
                if self.onlyqueues:
                    return

        # check if bot is set in event
        if not self.bot:
            rlog(10, 'event', 'no bot defined in event')
            return "no bot is defined in this event"

        # make response
        pretxt = origtxt
        if lt and not restxt:
            res = []

            # check if there are list in list

            for i in result:
                if type(i) == types.ListType or type(i) == types.TupleType:
                    try:
                        res.append(u' .. '.join(i))
                    except TypeError:
                        res.extend(i)
                else:
                    res.append(i)

            # if nritems is set ..
            result = res
            if nritems:
                if len(result) > 1:
                    pretxt += "(%s items) .. " % len(result)
            txtlist = result

            # prepend item number for results
            if not nr is False:
                try:
                    start = int(nr)
                except ValueError:
                    start = 0
                txtlist2 = []
                teller = start
                for i in txtlist:
                    txtlist2.append(u"%s) %s" % (teller, i))
                    teller += 1
                txtlist = txtlist2

            # convert results to encoding
            txtl = []
            for item in txtlist:
                txtl.append(toenc(item))
            txtlist = txtl

            # join result with dot 
            if dot == True:
                restxt = ' .. '.join(txtlist)
            elif dot:
                restxt = dot.join(txtlist)
            else:
                restxt = ' '.join(txtlist)

        # see if txt needs to be prepended
        if pretxt:
            try:
                restxt = pretxt + restxt
            except TypeError:
                rlog(10, 'eventbase', "can't add %s and %s" % (str(pretxt), str(restxt)))

        # if txt in result is filtered ignore the reuslt
        if self.filtered(restxt):
            return

        if restxt:
            return restxt
        else:
            return "no data found"

    def reply(self, *args, **kwargs):

        """
            reply to event. this version prints to stdout

            :param txt: txt to reply
            :type txt: string
            :param result: result list .. list of items to reply (is prepended to txt)
            :type result: string    
            :param nick: nick to reply to
            :type nick: string
            :param dot: whether results of result list should be seperated with a dot with ' ..' or the txt passed in as dot argument
            :type dot: boolean or string
            :param nritems: whether results should be numbered
            :type nritems: boolean
            :param nr: the number to start numerbering the result with
            :type nr: integer
            :param fromm: the user sending the reply
            :type fromm: nick or JID
            :param private: whether the reply should be in private
            :type private: boolean
            :param how: how the reply should be made (msg, notice, ctcp)
            :type how: string

        """
        resp = self.make_response(*args, **kwargs)

        if not resp:
            return

        self.bot.say(self.channel, resp)

    def missing(self, txt):

        """
            show what arguments are missing.

            :param txt: txt to add to missing response
            :type txt: string

            .. literalinclude:: ../../gozerbot/eventbase.py
                :pyobject: EventBase.missing

        """

        stats.up('events', 'missing')
        if self.origtxt:
            splitted = self.origtxt.split()
            if self.bot.nick in splitted[0]:
                try:
                    cmnd = splitted[1]
                except IndexError:
                    cmnd = splitted[0]
            elif 'cmnd' in splitted[0]:
                try:
                    cmnd = splitted[2]
                except IndexError:
                    cmnd = splitted[0]
            else:
                if self.msg:
                    cmnd = splitted[0]
                else:
                    if self.aliased:
                        cmnd = self.aliased
                    else:
                        cmnd = splitted[0][1:]
            self.reply(cmnd + ' ' + txt)
        else:
            self.reply('missing origtxt: %s' % txt)

    def ircstr(self):

        """
             old compat function. use str() now.

        """

        return str(self)

    def handle_error(self, data):

        """
            function to handler errors. override this.

            :param data: txt describing the error (can be different with inherited classes)
            :tyoe data: string (can be different with inherited classes)

        """

        rlog(10, self.name.error, 'ERROR: %s' % str(data))

    def done(self, txt=None):

        """
            reply with the txt 'done'.

            :param txt: txt to prepend to 'done'
            :type txt: string

        """

        if txt:
            self.reply('%s done' % txt)
        else:
            self.reply('done')

    def tojson(self):
        new = cpy(self)
        new.sock = 'setremote'
        new.bot = 'setremote'
        new.request = 'setremote'
        new.response = 'setremote'
        new.queues = []
        result = dumps(new)
        rlog(0, 'eventbase', 'tojson - %s' % str(result))
        return result

    def fromjsonstring(self, input):
        temp = loads(input)
        rlog(10, 'eventbase', str(temp))
        self.update(temp)
        return self

    def checkqueues(self, resultlist):

        """ 
            check if resultlist is to be sent to the queues. if so do it

            :param resultlist: list of results to send to queues
            :type resultlist: list
            :rtype: boolean

        """

        if self.queues:

            for queue in self.queues:
                for item in resultlist:
                    queue.put_nowait(item)

            return True

    def subelement(self, name):
       if self.subelements:
            rlog(10, 'eventbase', "got subelements")
            for node in self.subelements:
                try:
                    attr = getattr(node, name)
                    rlog(10, 'eventbase', "got subelement %s" % (name, str(attr)))
                    return attr.data
                except (AttributeError, TypeError):
                    continue   

# ============
# INIT SECTION
                
# default event used to initialise events
defaultevent = EventBase()


# END INIT
# ========
